/*
  日志落地模块的实现
  1、抽象落地基类
  2、派生子类(根据不同的落地方向进行派生)
  3、使用工厂模式进行创建和表示分离
*/
#include "util.cpp"
#include <fstream>
#include <memory>
#include <sstream>
#include <cassert>

namespace Mylog
{
    class Logsink
    {
        public:
            using ptr = std::shared_ptr<Logsink>;
            Logsink() {}
            virtual ~Logsink() {}
            virtual void log(const char* data, size_t len) = 0;
    };
    //落地方向：1、标准输出； 2、指定文件； 3、滚动文件（以大小）
    //1、标准输出
    class StdoutSink : public Logsink
    {
        void log(const char* data, size_t len)
        {
            std::cout.write(data, len);
        }
    };
    //2、指定文件
    class FileSink : public Logsink
    {
        public:
            FileSink(const std::string &pathname):_pathname(pathname)
            {
                //创建文件所在目录
                util::File::createDirectory(util::File::path(pathname)); 
                //创建并打开日志文件
                _ofs.open(_pathname, std::ios::binary | std::ios::app);
                assert(_ofs.is_open());
            }
            void log(const char* data, size_t len)
            {
                _ofs.write(data, len);
                assert( _ofs.good());
            }
        private:
            std::string _pathname;
            std::ofstream _ofs;
    };
    //3、滚动文件（以大小）
    class RollBySizeSink : public Logsink
    {
        public:
        //构造时传入文件名，并打开文件，将操作句柄管理起来
            RollBySizeSink(const std::string &basename, size_t max_size)
            :_basename(basename), 
            _max_fsize(max_size), 
            _cur_fsize(0),
            _name_count(0)
            {
                //创建文件所在目录
                std::string filename = creatNewFile();
                util::File::createDirectory(util::File::path(pathname)); 
                //创建并打开日志文件
                _ofs.open(pathname, std::ios::binary | std::ios::app);
                assert(_ofs.is_open());
            }
            void log(const char* data, size_t len)
            {
                if(_cur_fsize >= _max_fsize)
                {
                    std::string pathname creatNewFile();
                    _ofs.close();
                    _ofs.open(pathname, std::ios::binary | std::ios::app);
                    assert(_ofs.is_open());
                    _cur_fsize = 0;
                }
                _ofs.write(data, len);
                assert( _ofs.good());
                _cur_fsize += len;
            }
        private:
            std::string creatNewFile()//进行大小判断，超过指定大小创建新文件
            {
                time_t t = util::Date::now();
                struct tm lt;
                localtime_r(&t, &lt);
                std::stringstream filename;
                filename << _basename;
                filename << lt.tm_year + 1900;
                filename << lt.tm_mon + 1;
                filename << lt.tm_mday;
                filename << lt.tm_hour;
                filename << lt.tm_min;
                filename << lt.tm_sec;
                filename << '-';
                filename << _name_count++;
                filename << '.log';
                return filename.str();
            }
        private:
            size_t _name_count;
            std::string _basename;
            std::ofstream _ofs;
            size_t _max_fsize;//记录最大大小，超过就切换文件
            size_t _cur_fsize;//记录当前大小
    };

    class SinkFactory
    {
        public:
            template <typename SinkType, typename ...Aegs>
            static LogSink::ptr create(Args &&...args)
            {
                return std::make_shared<SinkType>(std::forward<Args>(args)...);
            }
    }

}